import numpy as np


class S_perceptron:
    """
    简单的感知器
    """
    def __init__(self, *, n: int, alpha: float=0.1):
        """
        :param n: 输入特征向量中的列数，记得是设计矩阵
        :param alpha: 学习率通常在α=0.1,0.01,0.001的范围内
        """
        self.w = np.random.randn(n + 1) / np.sqrt(n)
        self.alpha = alpha

    def step(self, *, x: np.ndarray) -> int:
        return 1 if x > 0 else 0

    def fit(self, *, x: np.ndarray, y: np.ndarray, epochs: int = 10):
        x = np.c_[x, np.ones((x.shape[0]))]
        for _ in np.arange(0, epochs):
            for data, target in zip(x, y):
                p = self.step(x=np.dot(data, self.w))
                if p != target[0]:
                    error = p - target
                    self.w += -self.alpha * error * data  # 权重更新

    def predict(self, *, x: np.ndarray, add_bias: bool = True):
        x = np.atleast_2d(x)
        if add_bias:
            x = np.c_[x, np.ones((x.shape[0]))]
        return self.step(x=np.dot(x, self.w))


if __name__ == '__main__':
    # x = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])  # or
    # y = np.array([[0], [1], [1], [1]])
    # x = np.array([[0, 0], [1, 0], [0, 1], [1, 1]])  # and
    # y = np.array([[0], [0], [0], [1]])
    x = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])  # xor
    y = np.array([[0], [1], [1], [0]])
    print('[info]:正在训练感知机...')
    sp = S_perceptron(n=x.shape[1], alpha=0.1)
    sp.fit(x=x, y=y, epochs=20)
    print('[info]:测试感知机中....')
    for data, target in zip(x, y):
        pred = sp.predict(x=data)
        print(f'[info]:{data=} ground-truth={target[0]} 预测={pred}')

